import Foundation

protocol DWModelType {}

extension DWModelType {
    static func dw_set(_ value: Any, _ ptr: UnsafeMutableRawPointer) {
        (value as? Self).flatMap { ptr.dw_bind(type: self).pointee = $0 }
    }
    
    static func dw_get(_ ptr: UnsafeMutableRawPointer) -> Any? {
        if ptr.dw_bind(type: Int.self).pointee == 0 {
            return nil
        }
        return ptr.dw_bind(type: self).pointee
    }
}

func dw_typeProxy(_ type: Any.Type) -> DWModelType.Type {
    return dw_bitCast(type1: (type, 0), type2: DWModelType.Type.self)
}

func dw_typeKey(_ type: Any.Type) -> UInt {
    return dw_bitCast(type1: type, type2: UInt.self)
}

func dw_bitCast <T1, T2>(type1: T1, type2: T2.Type) -> T2 {
    return unsafeBitCast(type1, to: type2)
}

func dw_model_convert(_ value : Any, info : DWJsonPropertyInfo) -> Any? {
    switch info.propertyType {
        case is DWJsonDictValue.Type:
            return dw_dict(value, type: info.propertyType, subType: info.subType)
            
        case is DWJsonArrayValue.Type:
            return dw_array(value, type: info.propertyType, subType: info.subType)
            
        case is DWJsonSetValue.Type:
            return dw_set(value, type: info.propertyType, subType: info.subType)
            
        default:
            return dw_model_normal_value(value, type: info.propertyType)
    }
}

func dw_model_normal_value(_ value : Any, type : Any.Type?) -> Any? {
    guard let _type = type else { return nil }
    switch _type {
        case is DWJsonStringValue.Type:
            return dw_string(value, type: _type)
            
        case is DWJsonNumberValue.Type:
            return dw_number(value, type: _type)
            
        case is DWJsonUrlValue.Type:
            return dw_url(value)
            
        case is DWJsonDataValue.Type:
            return dw_data(value, type: _type)
            
        case is DWJsonDateValue.Type:
            return dw_date(value)
            
        case is DWJsonConvert.Type:
            if let dic = dw_valueToDic(value), let convert = _type as? DWJsonConvert.Type {
                return convert.dw_model(dic)
            }
            return nil
            
        case is any RawRepresentable.Type:
            return (_type as? any RawRepresentable.Type)?.dw_enum(value)
            
        default:
            break
    }
    return nil
}

func dw_json_convert(_ value : Any?, info : DWJsonPropertyInfo) -> Any? {
    guard let _value = value else { return nil }
    switch info.propertyType {
        case is DWJsonConvert.Type:
            return (_value as? DWJsonConvert)?.dw_json()
            
        case is DWJsonDictValue.Type:
            return dw_json_get_dict(value, info.subType)
            
        case is DWJsonArrayValue.Type:
            return dw_json_get_array(value, info.subType)
            
        default:
            break
    }
    return _value
}

func dw_json_get_array(_ value : Any?, _ type : Any.Type?) -> [Any]? {
    guard let array = value as? [Any] else { return nil }
    if type is DWJsonConvert.Type {
        return array.compactMap {
            return ($0 as? DWJsonConvert)?.dw_json()
        }
    }
    if type is any RawRepresentable.Type {
        return array.compactMap {
            return ($0 as? (any RawRepresentable))?.rawValue
        }
    }
    if type is DWJsonDictValue.Type {
        let subType = (type as? DWJsonDictValue.Type)?.dw_subType()
        return array.compactMap {
            return dw_json_get_dict($0, subType)
        }
    }
    if type is DWJsonArrayValue.Type {
        let subType = (type as? DWJsonArrayValue.Type)?.dw_subType()
        return array.compactMap {
            return dw_json_get_array($0, subType)
        }
    }
    if type is DWJsonSetValue.Type {
        
    }
    return array
}

func dw_json_get_dict(_ value : Any?,_ type : Any.Type?) -> [String:Any]? {
    guard let dict = dw_valueToDic(value) else { return nil }
    if type is DWJsonConvert.Type {
        return dict.compactMapValues {
            return ($0 as? DWJsonConvert)?.dw_json()
        }
    }
    if type is DWJsonArrayValue.Type {
        let subType = (type as? DWJsonArrayValue.Type)?.dw_subType()
        return dict.compactMapValues {
            return dw_json_get_array($0, subType)
        }
    }
    if type is any RawRepresentable.Type {
        return dict.compactMapValues {
            return ($0 as? (any RawRepresentable))?.rawValue
        }
    }
    return dict
}
